﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Nizm0.Chat.Server
{
    public class Server
    {
        private TcpListener _listener = null;
        private TcpClient _client = null;
        private bool _isconnected = false;
        private BinaryReader _r = null;
        private BinaryWriter _w = null;

        private int _portChat = 8000, _portFileSend = 8001;
        IPAddress _ip;

        private List<Messages.User> _userList = new List<Messages.User>();

        #region MyRegion

        Thread _thrConnection;
        Thread _thrReceiving;

        
        //public void Start2()
        //{
        //    //_ip = new IPAddress();
        //    ThreadStart thrstart = new ThreadStart(Connection);
        //    _thrConnection = new Thread(thrstart);
        //    _thrConnection.Start();
        //    //Thread.Sleep(500);

        //}
        //public void Stop()
        //{
        //    if (_client != null) _client.Close();
        //    _listener.Stop();
        //    _isconnected = false;
        //}

        static bool _running = true;
        public void Recive()
        {
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Any, _portChat));
            server.Listen(int.MaxValue);
            while (_running)
            {
                Socket client = server.Accept();
                ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), client);
            }
            Console.ReadLine();
        }
        public void ProcessClient(object state)
        {
            Socket client = state as Socket;
                //coś
                byte[] buffer = new byte[8192];
                int count = 0;
                bool continueOrNot = false;
                string message = string.Empty;
                do
                {
                    count = client.Receive(buffer, 0, buffer.Length, SocketFlags.None);
                    Console.WriteLine("{0}\n{1}", client.RemoteEndPoint, client.LocalEndPoint);
                    //dekodowanie danych
                    //count powie ile bajtów już odebrałeś
                    Stream s = new MemoryStream(buffer);
                    var o = DeserializeObject(s);
                    //Console.WriteLine(o.GetType().ToString());
                    //var o1 = Convert.ChangeType(o, typeof(Messages.IMessage));
                    //string test = o.GetType().ToString();
                    //if (o.GetType() == typeof(Messages.Message))
                    //{
                        
                    //}
                    //Type a = o.GetType();
                    //Print(o, client);
                    //Thread.Sleep(1);
                    switch (o.GetType().ToString())
                    {
                        case "Messages.TextMessage":
                            {
                                //Messages.TextMessage textmessage = (Messages.TextMessage)o;
                                Print((Messages.TextMessage)o, client);
                                //messageObject.ReciveDate = DateTime.Now;
                                //Console.WriteLine("Wiadomosc od ip: {0}", client.RemoteEndPoint.ToString());
                                //Console.WriteLine(messageObject.MessageText + " \"" + (messageObject.ReciveDate - messageObject.SendDate).Milliseconds + "\"");
                                break;
                            }
                        case "Messages.LoginMessage":
                            {
                                //Messages.LoginMessage loginObject = (Messages.LoginMessage)o;
                                Print((Messages.LoginMessage)o, client);

                                //loginObject.MessageUser.Ip = (IPEndPoint)client.RemoteEndPoint;
                                //Console.WriteLine("Zalogowany użytkonwik: {0}\no ip: {1}:{2}", loginObject.MessageUser.ToString(), loginObject.MessageUser.Ip.Address.ToString(), loginObject.MessageUser.Ip.Port.ToString());
                                //_userList.Add(loginObject.MessageUser);
                                break;
                            }
                    }
                    message += Encoding.ASCII.GetString(buffer, 0, count);

                    continueOrNot = count == buffer.Length;
                } while (continueOrNot);
        }

        private void Print(Messages.IMessage o, Socket client)
        {
            Print(o.Get(), client);
        }
        public void Print(string str)
        {
            Console.WriteLine(DateTime.Now.ToLongTimeString() + ": " + str);
        }

        //public void Print(string str, Messages.MessageType type)
        //{
        //    switch (type)
        //    {
        //        case Messages.MessageType.client:
        //            {
        //                Console.WriteLine(DateTime.Now.ToLongTimeString() + ":\t" + str);
        //                break;
        //            }
        //        case Messages.MessageType.server:
        //            {
        //                Console.WriteLine(DateTime.Now.ToLongTimeString() + " : server\n" + str);
        //                break;
        //            }
        //        case Messages.MessageType.system:
        //            {
        //                Console.WriteLine(DateTime.Now.ToLongTimeString() + " : system\n" + str);
        //                break;
        //            }
        //        default:
        //            {
        //                Console.WriteLine(DateTime.Now.ToLongTimeString() + " -> " + type.ToString() + " : " + str);
        //                break;
        //            }
        //    }
        //}

        public void Print(Messages.TextMessage message, Socket client)
        {
            //Messages.TextMessage messageObject = (Messages.TextMessage)message;
            message.ReciveDate = DateTime.Now;
            Console.WriteLine("Wiadomosc od ip: {0}", client.RemoteEndPoint.ToString());
            Console.WriteLine(message.MessageText + " \"" + (message.ReciveDate - message.SendDate).Milliseconds + "\"");

            Send(message);
        }
        public void Print(Messages.LoginMessage message, Socket client)
        {
            //Messages.LoginMessage loginObject = (Messages.LoginMessage)message;
            message.Sender.Socked = client;
            //server.Listen(int.MaxValue);
            //while (_running)
            //{
            //    Socket client = server.Accept();
            //    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), client);
            //}
            message.Sender.Ip.Address = ((IPEndPoint)client.RemoteEndPoint).Address;
            Console.WriteLine("Zalogowany użytkonwik: {0}\no ip: {1}:{2}", message.Sender.ToString(), message.Sender.Ip.Address.ToString(), message.Sender.Ip.Port.ToString());
            
            _userList.Add(message.Sender);
            Send(message);
        }
        public void Send(Messages.TextMessage o)
        {
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            foreach (Messages.User user in _userList)
            {
                try
                {
                    //client.Connect(user.Ip);//_userList.Find(u => u.Name == o.Reciver.Name).Ip);
                    client = user.Socked;

                    byte[] buffer = SerializeObject(o);
                    int read = buffer.Length;
                    client.Send(buffer, 0, read, SocketFlags.None);
                }
                catch (Exception ex)
                {
                    _userList.RemoveAll(u => u.Ip == user.Ip);
                }
            }
        }
        public void Send(Messages.LoginMessage o)
        {
            foreach (Messages.User user in _userList)
            {
                //if(user.Socked.
                Socket client = user.Socked;//new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //client.Connect(_userList.Find(u => u.Name == o.Sender.Name).Ip);

                byte[] buffer = SerializeObject(o);
                int read = buffer.Length;
                client.Send(buffer, 0, read, SocketFlags.None);
            }
        }

        //private void Print(Messages.IMessage IMessage, Socket client)
        //{
        //    string a = IMessage.GetType().ToString();
        //    Print(IMessage, client);
        //}

        //private void Print(Messages.Message message, Socket client)
        //{
        //    string a = message.GetType().ToString();
        //    Print(message, client);
        //}

        //private void Connection()
        //{
        //    Print("Czekam na połączenie.");
        //    _listener = new TcpListener(_portChat);
        //    _listener.Start();
        //    while (!_listener.Pending())
        //    {
        //        if (!this._thrConnection.IsAlive)
        //        {
        //            if (_client != null) _client.Close();
        //            //_listener.Stop();
        //            _isconnected = false;
        //            return;
        //        }
        //    }

        //    _client = _listener.AcceptTcpClient();
        //    Print("Zarządano połączenia");
        //    NetworkStream stream = _client.GetStream();
        //    _w = new BinaryWriter(stream);
        //    _r = new BinaryReader(stream);
        //    if (_r.ReadString() == Messages.MessagesFromKlient.Require)
        //    {
        //        _w.Write(Messages.MessagesFromServer.OK); //wysyła wiadomość potwierdzającą
        //        Print("Połączono");
        //        _isconnected = true;
        //        ThreadPool.QueueUserWorkItem(Receiving);
        //        //ParameterizedThreadStart thrstart = new ParameterizedThreadStart(Receiving);
        //        //_thrReceiving = new Thread(thrstart);
        //        //_thrReceiving.Start(); //uruchamia wątek nasłuchujący na komunikat klienta
        //    }
        //    else
        //    {
        //        Print("Klient odrzuconuy");
        //        if (_client != null) _client.Close();
        //        //_listener.Stop();
        //        _isconnected = false;
        //    }

        //}

        //private void Receiving(object o)
        //{
        //    string str;
        //    while ((str = _r.ReadString()) != Messages.MessagesFromKlient.Disconnect)//oczekuj dopuki klient nie zarząda rozłączenia
        //    {
        //        Print(str);
        //    }
        //    Print("Rozłączono");
        //    _isconnected = false;
        //    _client.Close();
        //    //_listener.Stop();

        //}


        private byte[] SerializeObject(Messages.IMessage o)
        {
            MemoryStream stream = new MemoryStream();

            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            formatter.Serialize(stream, o);
            //stream.Close();
            return stream.GetBuffer();
        }
        private Messages.IMessage DeserializeObject(Stream stream)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            stream.Position = 0;
            var o = (Messages.IMessage)formatter.Deserialize(stream);
            //Console.WriteLine(o.GetType().ToString());
            return o;
        } 
        #endregion


        //#region From net

        //ManualResetEvent allDone = new ManualResetEvent(false);

        ///// <summary>
        ///// Starts a server that listens to connections
        ///// </summary>
        //public void Start()
        //{
        //    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //    listener.Bind(new IPEndPoint(IPAddress.Loopback, 1440));
        //    while (true)
        //    {
        //        Console.Out.WriteLine("Waiting for connection...");
        //        allDone.Reset();
        //        listener.Listen(100);
        //        listener.BeginAccept(Accept, listener);
        //        allDone.WaitOne(); //halts this thread             
        //    }
        //}

        ///// <summary>
        ///// Starts when an incomming connection was requested
        ///// </summary>            
        //public void Accept(IAsyncResult result)
        //{
        //    Console.Out.WriteLine("Connection received");
        //    Messages.TextMessage status = new Messages.TextMessage();
        //    status.Socket = ((Socket)result.AsyncState).EndAccept(result);
        //    status.Socket.BeginReceive(status.buffer, 0, status.buffer.Length, SocketFlags.None, Receive, status);
        //}

        ///// <summary>
        ///// Receives the data, puts it in a buffer and checks if we need to receive again.
        ///// </summary>            
        //public void Receive(IAsyncResult result)
        //{
        //    Messages.TextMessage status = (Messages.TextMessage)result.AsyncState;
        //    int read = status.Socket.EndReceive(result);
        //    if (read > 0)
        //    {
        //        for (int i = 0; i < read; i++)
        //        {
        //            status.TransmissionBuffer.Add(status.buffer[i]);
        //        }

        //        //we need to read again if this is true
        //        if (read == status.buffer.Length)
        //        {
        //            status.Socket.BeginReceive(status.buffer, 0, status.buffer.Length, SocketFlags.None, Receive, status);
        //            Console.Out.WriteLine("Past niet!");
        //        }
        //        else
        //        {
        //            Done(status);
        //        }
        //    }
        //    else
        //    {
        //        Done(status);
        //    }
        //}

        ///// <summary>
        ///// Deserializes and outputs the received object
        ///// </summary>            
        //public void Done(Messages.TextMessage status)
        //{
        //    Console.Out.WriteLine("Received: " + status.MessageText);
        //    Messages.TextMessage send = status.DeSerialize();
        //    Console.WriteLine(send.MessageText);
        //    allDone.Set(); //signals thread to continue 
        //    //So it jumps back to the first while loop and starts waiting for a connection again.
        //} 
        //#endregion
    }
}
